package world;

import behavior.Behavior;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import manager.Manager;
import org.encog.neural.networks.BasicNetwork;

public class World 
{
    //Size of x axis
    private int xSize;
    //Size of y axis
    private int ySize;
    //List of all veggies and carcasses in the world
    Edible[] edibleList;
    //List of all animals in the world
    Animal[] animalList;
    //List of all species in the simulation
    Species[] speciesList;
    //Number of living animals in each species
    int[] animalCount;
    //Which animals are attacking next turn
    int[] attackQueue;
    //Which animals are moving next turn
    int[] moveQueue;
    //Which animals are eating next turn
    int[] eatQueue;
    //Which animals are mating next turn
    int[] mateQueue;
    //Number of vegetation tiles that spawn each turn. Non-integer 
    //values result in different amounts per turn. For example a value 
    //of 1.5 will spawn 3 veggie edibles every 2 turns.
    private double veggieRate;
    //Used to track if a veggie should spawn on a given turn.
    private double veggieVar;
    //Which veggie is to have a veggie spawn near it.
    private int onVeggie;
    //Amount of food in each veggie
    private int veggieContent;
    //Reference back to the manager
    private Manager mgr;
    //Current turn
    private int turn;


    
    public static final int ATTACK = 1;
    public static final int MATE = 2;
    public static final int MOVE = 3;
    public static final int EAT = 4;
    public static final int WAIT = 5;

    
    /**
     * Argument:
     * -List of species in game
     * -size in x direction
     * -size in y direction
     * -veggie spawn rate
     * -veggie content
     * -current turn
     * -current veggie var
     * -Reference back to manager
     * Initializes world with these values
     */
    public World(Species[] list, int x, int y, double spawn, int cont,
            int turn, double veggieVar, Manager m, String file)
    {
        speciesList = list;
        animalCount = new int[list.length];
        for (int i = 0; i < animalCount.length; i++)
        {
            animalCount[i] = 0;
        }
        setxSize(x);
        setySize(y);
        setVeggieRate(spawn);
        setVeggieContent(cont);
        setTurn(turn);
        setVeggieVar(veggieVar);
        
        mgr = m;
        Properties props = new Properties();
        try
        {
            FileInputStream fis = new FileInputStream(file);
            props.load(fis);
            fis.close();

            int numAnimals = Integer.parseInt(props.getProperty("numAni"));
            animalList = new Animal[numAnimals];
            for (int i = 0; i < numAnimals; i++)
            {
                int life = Integer.parseInt(props.getProperty("ani" + i + "life"));
                int hunger = Integer.parseInt(props.getProperty("ani" + i + "hung"));
                int xLoc = Integer.parseInt(props.getProperty("ani" + i + "x"));
                int yLoc = Integer.parseInt(props.getProperty("ani" + i + "y"));
                int spec = Integer.parseInt(props.getProperty("ani" + i + "spec"));
                int preg = Integer.parseInt(props.getProperty("ani" + i + "preg"));
                int gen = Integer.parseInt(props.getProperty("ani" + i + "gen"));
                int birthday = Integer.parseInt(props.getProperty("ani" + i + "born"));
                int id = 0;//Integer.parseInt(props.getProperty("ani" + i + "id"));
                int offL;
                int[] offN;
                String[] net;
                if (preg >= 0)
                {
                    offL = Integer.parseInt(props.getProperty("ani" + i + "offLayers"));
                    offN = new int[offL];
                    for (int j = 0; j < offL; j++)
                    {
                        offN[j] = Integer.parseInt(props.getProperty("ani" + i + "nodes" + j));
                    }
                    net = props.getProperty("ani" + i + "offNet").split(",");
                    animalList[i].setOffspring(new Behavior(offL, offN, net));
                }
                else
                {
                    animalList[i].setOffspring(null);
                }
                animalList[i] = new Animal(xLoc, yLoc, spec, birthday, gen, speciesList[spec]);
                animalList[i].setLife(life);
                animalList[i].setHunger(hunger);
                animalList[i].setPregnancy(preg);
                animalCount[spec]++;
            }
            
            edibleList = new Edible[Integer.parseInt(props.getProperty("numEdi"))];
            for (int i = 0; i < edibleList.length; i++)
            {
                int xLoc = Integer.parseInt(props.getProperty("eat" + i + "x"));
                int yLoc = Integer.parseInt(props.getProperty("eat" + i + "y"));
                int amt = Integer.parseInt(props.getProperty("eat" + i + "fd"));
                boolean type = Boolean.parseBoolean(props.getProperty("eat" + i + "diet"));
                edibleList[i] = new Edible(xLoc, yLoc, amt, type);
            }
        }
        catch(IOException e)
        {
            mgr.unhandledException(e, "Unable to load World data from file.");
        }
    }
    
    /**
     * Argument: 
     * -x and y coords. 
     * Returns whether or not given x, y coords are empty
     */
    boolean isEmpty(int x, int y)
    {
        for (int i = 0; i < animalList.length; i++)
        {
            if (animalList[i].getX() == x && animalList[i].getY() == y)
            {
                return false;
            }
        }
        
        for (int i = 0; i < edibleList.length; i++)
        {
            if (edibleList[i].getX() == x && edibleList[i].getY() == y)
            {
                return false;
            }
        }
        return true;
    }

    /**
     * Argument:
     * -x value of coordinates
     * -y value of coordinates
     * Returns: 
     * -Entity at given coordinates
     * Checks animal list and then entity list for entity at given 
     * coordinates.
     */
    public Entity getOccupant(int x, int y)
    {
        for (int i = 0; i < animalList.length; i++)
        {
            if (animalList[i].getX() == x && animalList[i].getY() == y)
            {
                return animalList[i];
            }
        }
        
        for (int i = 0; i < edibleList.length; i++)
        {
            if (edibleList[i].getX() == x && edibleList[i].getY() == y)
            {
                return edibleList[i];
            }
        }
        return null;
    }

    /**
     * Passes index of animal to kill. 
     * Removes that animal from animal list and generates a 
     * carcass for it.
     **/
    void kill(int killMe)
    {
        int thisSpec = animalList[killMe].getSpecies();
        Edible newbie = new Edible(animalList[killMe].getX(), 
                animalList[killMe].getY(), 
                speciesList[thisSpec].getCarcassFood(),
                true);
        removeAnimalFromList(killMe);
        mgr.kill(killMe);
        addEdibleToList(newbie);
        //May be time to implement the mercy rule.
        if (animalCount[thisSpec] < speciesList[thisSpec].getMercyAmt())
        {
            int mama = 0;
            int papa = 1;
            for (int i = 2; i < animalList.length; i++)
            {
                if (animalList[i].getGeneration() > animalList[mama].getGeneration() 
                        && animalList[i].getSpecies() == thisSpec)
                {
                    papa = mama;
                    mama = i;
                }
            }
            BasicNetwork noob = mgr.generateBehavior(mama, papa);
            animalList[mama].setOffspring(new Behavior(noob));
            animalList[mama].setPregnancy(0);
        }
    }
    
    private void addEdibleToList(Edible newbie)
    {
        Edible[] newList = new Edible[edibleList.length + 1];
        for (int cntr = 0; cntr < edibleList.length; cntr++)
        {
            newList[cntr] = edibleList[cntr];
        }
        newList[edibleList.length] = newbie;
        edibleList = newList;
    }
    
    private void addAnimalToList(Animal newbie)
    {
        Animal[] newList = new Animal[animalList.length + 1];
        for (int cntr = 0; cntr < animalList.length; cntr++)
        {
            newList[cntr] = animalList[cntr];
        }
        newList[animalList.length] = newbie;
        animalList = newList;
        animalCount[newbie.getSpecies()]++;
    }
    
    private void removeEdibleFromList(int n)
    {
        Edible[] newList = new Edible[edibleList.length - 1];
        int x = 0;
        for(int cntr = 0; cntr < edibleList.length; cntr++)
        {
            if (cntr != n)
            {
                newList[x++] = edibleList[cntr];
            }
        }
        this.edibleList = newList;
    }
    
    private void removeAnimalFromList(int n)
    {
        Animal[] newList = new Animal[animalList.length - 1];
        int x = 0;
        int species = -1;
        for(int cntr = 0; cntr < animalList.length; cntr++)
        {
            if (cntr != n)
            {
                newList[x++] = animalList[cntr];
            }
            else
            {
                species = animalList[cntr].getSpecies();
            }
        }
        this.animalList = newList;
        animalCount[species]--;
    }

    /**
     * Passes index of edible to remove.
     * Removes that edible from edible list.
     */
    void consume(int eatMe)
    {
        removeEdibleFromList(eatMe);
    }

    /**
     * Argument: index of animal seeing
     * Returns: 2-d array where second dimension contains two 
     * elements, x and y values of the space being viewed.
     * Selects animal with passed index from animal list. Loops 
     * from -sightRad to sightRad and within that loop is second 
     * loop from abs(index of first) - sightRad to 
     * sightRad - abs(index of first). Adds indices to animal 
     * location and puts them in second dimension of returning array.
     */
    public int[][] getAnimalView(int viewerIndex)
    {
        int sightRad = speciesList[animalList[viewerIndex].getSpecies()].getSightRad();
        int baseX = animalList[viewerIndex].getX();
        int baseY = animalList[viewerIndex].getY();
        int index = 0;
        int[][] returnMe = new int[2*sightRad*sightRad + 2*sightRad][];
        for (int a = -1*sightRad; a <= sightRad; a++)
        {
            for (int b = Math.abs(a) - sightRad; b <= sightRad - Math.abs(a); b++)
            {
                if (a != 0 || b != 0)
                {
                    returnMe[index] = new int[2];
                    returnMe[index][0] = baseX + a;
                    returnMe[index][1] = baseY + b;
                    index++;
                }
            }
        }
        return returnMe;
    }

    /**
     * Argument: 
     * -index of animal acting
     * -action to be performed
     * Sets action property on animal with passed index to passed action
     */
    public void updateAction(int actor, int action)
    {
        animalList[actor].setAction(action);
    }

    /**
     * Argument:
     * -Index of animal targeting
     * -X value of target
     * -Y value of target
     * Sets actionX and actionY properties on animal with passed index.
     */
    public void updateTarget(int actor, int targetX, int targetY)
    {
        int rad = speciesList[animalList[actor].getSpecies()].getSightRad();
        animalList[actor].setActionX(targetX % rad);
        animalList[actor].setActionY(targetY % rad);
    }

    /**
     * Adds veggieSpawnRate to veggieVar. If veggieVar is more 
     * than 1, subtracts the largest int value less than veggieVar 
     * from veggieVar.
     * In a loop from 1 to this int value the veggie whos index 
     * matches onVeggie is used as the base. The turn is modded 
     * by the distance from the veggie (starts at 2, increases in 
     * increments of 2 if each space at that distance is occupied) 
     * and that modulo determines which space to first attempt to 
     * place the veggie in. Proceed clockwise if a given space is 
     * occupied.
     */
    public void spawnVeggies()
    {
        veggieVar = veggieRate + veggieVar;
        if (veggieVar >= 1)
        {
            int veggieInt = (int)veggieVar;
            veggieVar = veggieVar - veggieInt;
            for (int i = 0; i < veggieInt; i++)
            {
                Edible baseVeggie = edibleList[getOnVeggie()];
                int baseX = baseVeggie.getX();
                int baseY = baseVeggie.getY();
                int dist = 2;
                int turnMod = getTurn() % 8*dist;
                int[] spot;
                do
                {
                    spot = mgr.getFromCircle(dist, turnMod);
                    spot[0] = spot[0] + baseX;
                    spot[1] = spot[1] + baseY;
                    //These four conditionals ensure that the new veggie remains
                    //within our agreed-upon grid size.
                    if (spot[0] > getxSize()/2)
                    {
                        spot[0] = spot[0] - getxSize();
                    }
                    if (spot[0] < -1*getxSize()/2)
                    {
                        spot[0] = spot[0] + getxSize();
                    }
                    
                    if (spot[1] > getySize()/2)
                    {
                        spot[1] = spot[1] - getySize();
                    }
                    if (spot[1] < -1*getySize()/2)
                    {
                        spot[1] = spot[1] + getySize();
                    }
                    turnMod++;
                    if (turnMod >= 8*dist)
                    {
                        dist = dist + 2;
                        turnMod = getTurn() % 8*dist;
                    }
                } while (!isEmpty(spot[0], spot[1]));
                Edible newbie = new Edible(spot[0], spot[1], veggieContent, false);
                addEdibleToList(newbie);
            }
        }
    }

    /**
     * Loops through each animal and performs their actions
     * After that, loop through all animals killing any with 0 or 
     * less health or hunger and placing a carcass with food equal 
     * to their species carcass food in the space they were occupying.
     * Calls giveBirth passing index of current animal if that animal 
     * has pregnancy at 0. If pregnancy > 0, decrement pregnancy.
     * Loop through all edibles and remove any with 0 food.
     */
    public void finalizeTurn()
    {
        //Finalize turn for each animal.
        
        for (int i = 0; i < animalList.length; i++)
        {
            //The animal is given the chance to regen before it is checked to see
            //if it died. I need to know how much it regen'd so I can take that 
            //into account when checking its health.
            int regen = 0;
            Animal a = animalList[i];
            System.out.print(a.getAction() + ", " + a.getActionX() + ", " + a.getActionY());
            //This condition checks the animal's action.
            if (a.getAction() == ATTACK)
            {
                if (!isEmpty(a.getActionX(), a.getActionY()))
                {
                    Entity target = getOccupant(a.getActionX(), a.getActionY());
                    //They say you're not supposed to do this, but it looks fine
                    //to me.
                    if (target instanceof Animal)
                    {
                        attack(a, (Animal)target);
                    }
                    else
                    {
                        wait(a);
                        regen = speciesList[a.getSpecies()].getRegenRate();
                    }
                }
                else
                {
                    wait(a);
                    regen = speciesList[a.getSpecies()].getRegenRate();
                }
            }
            else if (a.getAction() == MATE)
            {
                if (!isEmpty(a.getActionX(), a.getActionY()))
                {
                    Entity target = getOccupant(a.getActionX(), a.getActionY());
                    //They say you're not supposed to do this, but it looks fine
                    //to me.
                    if (target instanceof Animal && ((Animal)target).getSpecies() == a.getSpecies() && ((Animal)target).getPregnancy() < 0)
                    {
                        mate(a, i, (Animal)target, getOccupantIndex(a.getActionX(), a.getActionY()));
                    }
                    else
                    {
                        wait(a);
                        regen = speciesList[a.getSpecies()].getRegenRate();
                    }
                }
                else
                {
                    wait(a);
                    regen = speciesList[a.getSpecies()].getRegenRate();
                }
            }
            else if (a.getAction() == MOVE)
            {
                move(a, a.getActionX(), a.getActionY());
            }
            else if (a.getAction() == EAT)
            {
                if (!isEmpty(a.getActionX(), a.getActionY()))
                {
                    Entity target = getOccupant(a.getActionX(), a.getActionY());
                    //They say you're not supposed to do this, but it looks fine
                    //to me.
                    if (target instanceof Edible && a.getHunger() < speciesList[a.getSpecies()].getMaxHunger())
                    {
                        eat(a, (Edible)target);
                    }
                    else
                    {
                        wait(a);
                        regen = speciesList[a.getSpecies()].getRegenRate();
                    }
                }
                else
                {
                    wait(a);
                    regen = speciesList[a.getSpecies()].getRegenRate();
                }
            }
            else
            {
                wait(a);
                regen = speciesList[a.getSpecies()].getRegenRate();
            }
            
            //If the animal's current life, minus however much it regen'd this
            //turn is LTE 0, kill off the animal.
            if (a.getLife() - regen <= 0)
            {
                kill(i);
            }
            
            if (a.getHunger() <= 0)
            {
                kill(i);
            }
            
            //If pregnancy is less than 0, animal is not pregnant.
            if (a.getPregnancy() > -1)
            {
                //Pregnancy is zero, time to go into labor!
                if (a.getPregnancy() == 0)
                {
                    giveBirth(i);
                }
                else
                {
                    a.setPregnancy(a.getPregnancy()-1);
                }
            }
        }
        System.out.println(";");
        for (int i = 0; i < edibleList.length; i++)
        {
            if (edibleList[i].getFood() <= 0)
            {
                consume(i);
            }
        }
    }

    /**
     * Argument: 
     * -index of animal giving birth
     * calls addBehavior in Manager, passing offspring value of 
     * passed animal. Calls spawnAnimal passing the species of the 
     * birthing animal and the space to its left.
     */
    void giveBirth(int birther)
    {
        Animal mom = animalList[birther];
        if (isEmpty(mom.getX()-1, mom.getY()))
        {
            mgr.addBehavior(mom.getOffspring());
            spawnAnimal(mom.getSpecies(), mom.getX()-1, mom.getY(), mom.getGeneration());
        }
        mom.setPregnancy(-1);
        mom.setOffspring(null);
    }

    /**
     * Argument:
     * -Species of animal to spawn
     * -x value of spawn location
     * -y value of spawn location
     * -Generation of animal to spawn
     * Adds an animal at the passed location. If that location is 
     * occupied, moves up one and tries there. Continues in a square 
     * around the space to the passed location’s right. If nothing 
     * can be found in those eight spaces, animal is not spawned. 
     * Checks generation against species generation and if the new 
     * gen is higher, makes that the new species peak generation.
     */
    void spawnAnimal(int species, int x, int y, int gen)
    {
        int count = 0;
        while (!isEmpty(x, y) && count < 8)
        {
            switch (count)
            {
                case 0:
                    y+=1;
                    break;
                case 1:
                    x+=1;
                    break;
                case 2:
                    y-=1;
                    break;
                case 3:
                    y-=1;
                    break;
                case 4:
                    x-=1;
                    break;
                case 5:
                    x-=1;
                    break;
                case 6:
                    y+=1;
                    break;
                case 7:
                    y+=1;
            }
            count++;
        }
        if (count < 8)
        {
            Animal newbie = new Animal(x, y, species, getTurn(), gen, speciesList[species]);
            addAnimalToList(newbie);
            if (speciesList[species].getMaxGen() < gen)
            {
                speciesList[species].setMaxGen(gen);
            }
        }
    }
    
    /**
     * Argument:
     * -Index of attacking animal
     * -Index of animal being attacked
     * If attacking animal is not next to animal being attacked, 
     * call move passing attacking animal and location of defending 
     * animal.
     * If attacker is now 1 space away from attackee
     *  Removes attack power of attacking animal from health of 
     *  second animal. 
     *  Subtracts attacker’s attackCost from hunger
     * Otherwise, subtracts moveCost from attacker’s hunger
     */
    void attack(Animal attacker, Animal attackee)
    {
        //Move function is going to subtract hunger. Prebate it so the animal
        //doesn't get penalized for moving.
        move(attacker, attackee.getX(), attackee.getY());
        attacker.setHunger(attacker.getHunger() + speciesList[attacker.getSpecies()].getMoveHunger());
        if (Math.abs(attacker.getX() - attackee.getX()) <= 1 && Math.abs(attacker.getY() - attackee.getY()) <= 1)
        {
            attackee.setLife(attackee.getLife() - speciesList[attacker.getSpecies()].getAttackPower());
            attacker.setHunger(attacker.getHunger() - speciesList[attacker.getSpecies()].getFightHunger());
        }
        else
        {
            attacker.setHunger(attacker.getHunger() - speciesList[attacker.getSpecies()].getMoveHunger());
        }
    }

    /**
     * Argument
     * -Index of mater
     * -index of matee
     * If mater is not next to matee, call move passing mater and 
     * location of matee.
     * If mater is now 1 space away from matee
     *  Calls generateBehavior in Manager with the indices of 
     *  the mater and matee and sets the returned value to matee’s 
     *  offspring attribute.
     *  subtracts mater’s mateCost from hunger
     * Otherwise, subtracts moveCost from attacker’s hunger
     */
    void mate(Animal a, int i, Animal b, int j)
    {
        //Move function is going to subtract hunger. Prebate it so the animal
        //doesn't get penalized for moving.
        move(a, b.getX(), b.getY());
        a.setHunger(a.getHunger() + speciesList[a.getSpecies()].getMoveHunger());
        if (Math.abs(a.getX() - b.getX()) <= 1 && Math.abs(a.getY() - b.getY()) <= 1 )
        {
            a.setHunger(a.getHunger() - speciesList[a.getSpecies()].getMateHunger());
            b.setPregnancy(speciesList[b.getSpecies()].getGestation());
            b.setOffspring(new Behavior(mgr.generateBehavior(i, j)));
        }
        else
        {
            a.setHunger(a.getHunger() - speciesList[a.getSpecies()].getMoveHunger());
        }
    }

    /**
     * Argument:
     * -index of mover
     * -x value of new location for mover
     * -y value of new location for mover
     * Checks spaces from mover.getX(), mover.getY() to mover.getX(), dest.getY(), then 
     * from mover.getX(), dest.getY() to dest.getY(), dest.getY() for obstructions. If 
     * none exist move agent to destination. If one is encountered 
     * record how far mover could travel along that path, then check 
     * moving horizontally before vertically. Use whichever got the 
     * mover further.
     * If the mover is unable to reach the destination, either due to 
     * distance or obstacles, move them to the closest possible point.
     * Otherwise, subtract’s mover’s moveCost from hunger
     */
    void move(Animal a, int destX, int destY)
    {
        int farthestX = a.getX();
        int farthestY = a.getY();
        if (a.getX() < destX)
        {
            for (int i = a.getX()+1; i <= destX; i++)
            {
                if (isEmpty(i, a.getY()))
                {
                    farthestX = i;
                }
                else
                {
                    break;
                }
            }
        }
        else
        {
            for (int i = a.getX()-1; i >= destX; i--)
            {
                
                if (isEmpty(i, a.getY()))
                {
                    farthestX = i;
                }
                else
                {
                    break;
                }
            }
        }
        
        if (a.getY() < destY)
        {
            for (int i = a.getY()+1; i <= destY; i++)
            {
                if (isEmpty(farthestX, i))
                {
                    farthestY = i;
                }
                else
                {
                    break;
                }
            }
        }
        else
        {
            for (int i = a.getY()-1; i >= destY; i--)
            {
                
                if (isEmpty(farthestX, i))
                {
                    farthestX = i;
                }
                else
                {
                    break;
                }
            }
        }
        
        //If we couldn't reach the destination, we're going to try again from
        //another direction.
        if ((farthestX != destX || farthestY != destY) &&
                (!isEmpty(destX, destY) && (Math.abs(farthestX - destX) >= 1 
                || Math.abs(farthestY - destY) >= 1)))
        {

            int prevFarX = farthestX;
            int prevFarY = farthestY;
            farthestX = a.getX();
            farthestY = a.getY();
            if (a.getY() < destY)
            {
                for (int i = a.getY()+1; i <= destY; i++)
                {
                    if (isEmpty(a.getX(), i))
                    {
                        farthestY = i;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            else
            {
                for (int i = a.getY()-1; i >= destY; i--)
                {

                    if (isEmpty(a.getX(), i))
                    {
                        farthestY = i;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            if (a.getX() < destX)
            {
                for (int i = a.getX()+1; i <= destX; i++)
                {
                    if (isEmpty(i, farthestY))
                    {
                        farthestX = i;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            else
            {
                for (int i = a.getX()-1; i >= destX; i--)
                {

                    if (isEmpty(i, farthestY))
                    {
                        farthestX = i;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            //Technically these are the squares of the distances, but they compare
            //the same.
            double thisDist = Math.pow(farthestX - destX, 2) + Math.pow(farthestY - destY, 2);
            double prevDist = Math.pow(prevFarX - destX, 2) + Math.pow(prevFarY - destY, 2);
            
            if (prevDist < thisDist)
            {
                farthestX = prevFarX;
                farthestY = prevFarY;
            }
        }
        
        a.setX(farthestX);
        a.setY(farthestY);
        
        a.setHunger(a.getHunger() - speciesList[a.getSpecies()].getMoveHunger());
    }

    /**
     * Argument:
     * -Index of eater
     * -Index of edible to be consumed
     * If eater is not next to edible to be consumed, call move 
     * with index of eater and location of edible.
     *  If eater is now 1 space away from edible
     *      If edible’s remaining food >= eater’s max hunger - hunger, 
     *      subtract that difference from edible’s remaining food and 
     *      set eater’s hunger to max
     *  Otherwise add remaining food to eater’s hunger, and set 
     *  edible’s food to 0.
     * Otherwise, subtract eater’s moveCost from hunger
     */
    void eat(Animal a, Edible b)
    {
        //Move function is going to subtract hunger. Prebate it so the animal
        //doesn't get penalized for moving.
        move(a, b.getX(), b.getY());
        a.setHunger(a.getHunger() + speciesList[a.getSpecies()].getMoveHunger());
        if (Math.abs(a.getX() - b.getX()) <= 1 && Math.abs(a.getY() - b.getY()) <= 1 )
        {
            int amtToEat = speciesList[a.getSpecies()].getMaxHunger() - a.getHunger();
            if (b.getFood() >= amtToEat)
            {
                b.setFood(b.getFood() - amtToEat);
                a.setHunger(speciesList[a.getSpecies()].getMaxHunger());
            }
            else
            {
                a.setHunger(a.getHunger() + b.getFood());
                b.setFood(0);
            }
        }
        else
        {
            a.setHunger(a.getHunger() - speciesList[a.getSpecies()].getMoveHunger());
        }
    }

    /**
     * Argument: 
     * -index of waiter
     * Add waitHealth to waiter’s health. If result is greater than 
     * max, set to max.
     * Subtract waiter’s waitCost from hunger
     */
    void wait(Animal a)
    {
        a.setHunger(a.getHunger() - speciesList[a.getSpecies()].getWaitHunger());
        a.setLife(a.getLife() + speciesList[a.getSpecies()].getRegenRate());
        if (a.getLife() > speciesList[a.getSpecies()].getMaxLife())
        {
            a.setLife(speciesList[a.getSpecies()].getMaxLife());
        }
    }
    
    /**
     * Argument:
     * -x and y coords
     * Returns
     * -index of occupant
     * Checks animal list, then edible list for something with 
     * those x and y coords, then returns the index. Gives no 
     * indication of if occupant is animal or edible.
     */
    int getOccupantIndex(int x, int y)
    {
        for (int i = 0; i < animalList.length; i++)
        {
            if (animalList[i].getX() == x && animalList[i].getY() == y)
            {
                return i;
            }
        }
        for (int i = 0; i < edibleList.length; i++)
        {
            if (edibleList[i].getX() == x && edibleList[i].getY() == y)
            {
                return i;
            }
        }
        return -1;
    }

    /**
     * Returns:
     * -length of animalList
     */
    public int getNumAnimals()
    {
        return animalList.length;
    }
    
    /**
     * Returns:
     * -length of edibleList
     */
    public int getNumEdibles()
    {
        return edibleList.length;
    }
    
    /**
     * Returns:
     * -Number of veggies in entire world
     */
    public int getNumEdibleByType(boolean type)
    {
        int i = 0;
        for (int j = 0; j < edibleList.length; j++)
        {
            if (edibleList[j].getType()==type)
            {
                i++;
            }
        }
        return i;
    }

    /**
     * Returns:
     * -Array of animal counts for each species 
     */
    public int[] animalCountBySpecies()
    {
        return animalCount;
    }


    
    public int getNumSpecies()
    {
        return speciesList.length;
    }
    
    /**
     * Argument:
     * -Index of requested animal
     * Returns:
     * -Animal at passed index
     */
    public Animal getAnimalByIndex(int ndx)
    {
        return animalList[ndx];
    }
    
    /**
     * Argument:
     * -Index of requested edible
     * Returns:
     * -Edible at passed index
     */
    public Edible getEdibleByIndex(int ndx)
    {
        return edibleList[ndx];
    }
    


    /**
     * @return the xSize
     */
    public int getxSize() {
        return xSize;
    }

    /**
     * @param xSize the xSize to set
     */
    public void setxSize(int xSize) {
        this.xSize = xSize;
    }

    /**
     * @return the ySize
     */
    public int getySize() {
        return ySize;
    }

    /**
     * @param ySize the ySize to set
     */
    public void setySize(int ySize) {
        this.ySize = ySize;
    }

    /**
     * @return the veggieRate
     */
    public double getVeggieRate() {
        return veggieRate;
    }

    /**
     * @param veggieRate the veggieRate to set
     */
    public void setVeggieRate(double veggieRate) {
        this.veggieRate = veggieRate;
    }

    /**
     * @return the veggieVar
     */
    public double getVeggieVar() {
        return veggieVar;
    }

    /**
     * @param veggieVar the veggieVar to set
     */
    public void setVeggieVar(double veggieVar) {
        this.veggieVar = veggieVar;
    }

    /**
     * Increment onVeggie
     * @return the onVeggie
     */
    public int getOnVeggie() 
    {
        int ofTheJedi;
        if (onVeggie <= edibleList.length)
        {
            ofTheJedi = onVeggie;
            onVeggie++;
        }
        else
        {
            ofTheJedi = edibleList.length - 1;
            onVeggie = 0;
        }
        return ofTheJedi;
    }

    /**
     * @param onVeggie the onVeggie to set
     */
    public void setOnVeggie(int onVeggie) {
        this.onVeggie = onVeggie;
    }

    /**
     * @return the veggieContent
     */
    public int getVeggieContent() {
        return veggieContent;
    }

    /**
     * @param veggieContent the veggieContent to set
     */
    public void setVeggieContent(int veggieContent) {
        this.veggieContent = veggieContent;
    }

    /**
     * @return the turn
     */
    public int getTurn() {
        return turn;
    }

    /**
     * @param turn the turn to set
     */
    public void setTurn(int turn) {
        this.turn = turn;
    }
    
    public Species getSpeciesByIndex(int i)
    {
        return speciesList[i];
    }

}
